#include <math.h>
#include <allegro.h>

#define PI (atan(1)*4)
#define COUNTOF(x) ( sizeof(x) / sizeof((x)[0]) )

struct vertice {
  float x, y, z;
};

struct vertice map[31*31];
struct vertice cell;
struct vertice *p_selection;

float rotation = PI*0.25;
float elevation = 1.1;
float zoom = 30.0;
float right_x, right_y;
float front_x, front_y;
float up_x, up_y; 

float default_sprite_width = 1.5;
float default_sprite_height = 3.0;

float x, y, z, screen_x, screen_y;

float sprite_x = 3;
float sprite_y = 3;
float sprite_width, sprite_height;

int poly[10];

int i, j;

PALETTE palette;
BITMAP *buffer;
BITMAP *z_buffer;
BITMAP *image;

void make_poly_dot(int *poly, int index, float x, float y)
{
  z = (sin(x/2) + cos(y/2)) * 2;
  screen_x = x * right_x + y * front_x + z * up_x;
  screen_y = x * right_y + y * front_y + z * up_y;
  screen_x += SCREEN_W * 0.5;
  screen_y += SCREEN_H * 0.5;
  poly[index] = (int)floor(screen_x);
  poly[index+1] = (int)floor(screen_y); 
}

void make_poly(int *poly, float x, float y)
{
  make_poly_dot(poly, 0, x-0.5, y-0.5);
  make_poly_dot(poly, 2, x-0.5, y+0.5);
  make_poly_dot(poly, 4, x+0.5, y+0.5);
  make_poly_dot(poly, 6, x+0.5, y-0.5);
  make_poly_dot(poly, 8, x-0.5, y-0.5);
}

int main(void)
{

  if (allegro_init() != 0)
    return 1;
  install_timer();
  install_keyboard();
  install_mouse();

  set_color_depth(32);

  if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0) != 0) 
  {
   if (set_gfx_mode(GFX_SAFE, 320, 200, 0, 0) != 0)
    {
      set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
      allegro_message("Unable to set any graphic mode\n%s\n", allegro_error);
      return 1;
    }
  }
  buffer = create_bitmap(SCREEN_W, SCREEN_H);
  z_buffer = create_bitmap(SCREEN_W, SCREEN_H);

  image = load_bitmap("woman.bmp", palette);
  

  /* painting the ground */
  i = 0;
  for (x=-15.0; x<=15.0; x+=1.0)
  {
    for (y=-15.0; y<=15.0; y+=1.0)
    {
      map[i].x = x;
      map[i].y = y;
      map[i].z = (sin(x/2) + cos(y/2)) * 2;
      i++;
    }
  }

  while (!key[KEY_ESC])
  {
    /* -- INPUTS -- */
   
    if (key[KEY_4_PAD]) rotation += 0.01;
    if (key[KEY_6_PAD]) rotation -= 0.01;    
    if (key[KEY_8_PAD]) elevation += 0.01;
    if (key[KEY_2_PAD]) elevation -= 0.01;    
    if (key[KEY_EQUALS] || key[KEY_PLUS_PAD]) zoom *= 1.01;    
    if (key[KEY_MINUS] || key[KEY_MINUS_PAD]) zoom *= 0.99;

    /* mouse events */ 
    if (mouse_b)
    {
      p_selection = getpixel(z_buffer, mouse_x, mouse_y);
      if (p_selection)
      {
        sprite_x = p_selection->x;
        sprite_y = p_selection->y;
      }
    }

    /* precalculated vectors */
    right_x = cos(rotation) * zoom;
    right_y = sin(rotation) * cos(elevation) * zoom;
    front_x = -sin(rotation) * zoom;
    front_y = cos(rotation) * cos(elevation) * zoom;
    up_x = 0.0 * zoom;
    up_y = sin(elevation) * zoom;

    sprite_width = default_sprite_width * zoom;
    sprite_height = default_sprite_height * zoom;

    /* -- PAINTINGS -- */

    clear_bitmap(buffer);
    clear_bitmap(z_buffer);

    /* painting the ground */
    for (i=0; i<COUNTOF(map); i++)
    {
      x = map[i].x;
      y = map[i].y;

      /* drawing the ground */
      make_poly(poly, x, y);
      polygon(z_buffer, 5, poly, (int) &map[i]);

      if (p_selection == &map[i])
        polygon(buffer, 5, poly, makecol(200,200,0));
      else 
        polygon(buffer, 5, poly, makecol(0,200,0));

      for (j = 2; j < 10; j+=2)
      {
        line(buffer,
             poly[j-2],
             poly[j-1],
             poly[j],
             poly[j+1],
             makecol(0,100,0));
      }

      /* drawing the sprite */
      if (sprite_x == x && sprite_y == y)
      {
        z = map[i].z;

        /* difference between the ground and the character foot */
        z += 0.2;

        screen_x = x * right_x + y * front_x + z * up_x;
        screen_y = x * right_y + y * front_y + z * up_y; 

        screen_x -= (sprite_width * 0.5);
        screen_y -= sprite_height;
        BITMAP *stretched;
        stretch_sprite(buffer, image,
                      (int)floor(screen_x + SCREEN_W * 0.5), 
                      (int)floor(screen_y + SCREEN_H * 0.5), 
                      sprite_width, sprite_height);
      }
    }

    /* painting the cursor */
    triangle(buffer, 
             mouse_x, mouse_y, 
             mouse_x+15, mouse_y, 
             mouse_x, mouse_y+15, 
             makecol(255, 255, 255));
    line(buffer, mouse_x, mouse_y, mouse_x+15, mouse_y, 0);
    line(buffer, mouse_x, mouse_y, mouse_x, mouse_y+15, 0);
    line(buffer, mouse_x+15, mouse_y, mouse_x, mouse_y+15, 0);

    /* painting info */
    textprintf_ex(buffer, font, 10, 10, makecol(255, 255, 255), -1, 
                  "mouse....: (%d, %d)", mouse_x, mouse_y);
    if (p_selection)
      textprintf_ex(buffer, font, 10, 20, makecol(255, 255, 255), -1, 
                  "selection: (%02.1f, %02.1f, %02.1f)", 
                  p_selection->x, p_selection->y, p_selection->z);

    blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
    rest(20);
  }

  destroy_bitmap(buffer);
  return 0;
}

END_OF_MAIN();

